/*
 * File      : fh_dma.c
 * This file is part of FH8620 BSP for RT-Thread distribution.
 *
 * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd.
 * All rights reserved
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 *  Visit http://www.fullhan.com to get contact with Fullhan.
 *
 * Change Logs:
 * Date           Author       Notes
 */

/*****************************************************************************
 *  Include Section
 *  add all #include here
 *****************************************************************************/
//#include "drivers/fh_dma.h"
#include "fh_dma.h"
#include "mmu.h"
#include "drivers/dma.h"
#include <stdint.h>
#include <rtdevice.h>
#include <rthw.h>
#include "fh_arch.h"
#include "mmu.h"
#include "fh_def.h"
/*****************************************************************************
 * Define section
 * add all #define here
 *****************************************************************************/
//#define DMA_DEBUG
#if defined(DMA_DEBUG) && defined(RT_DEBUG)

#define FH_DMA_DEBUG(fmt, args...) rt_kprintf(fmt, ##args);
#else
#define FH_DMA_DEBUG(fmt, args...)
#endif

#define DMA_REG_BASE (0xEE000000)
#define DMA_CONTROLLER_NUMBER (1)

#define WORK_QUEUE_STACK_SIZE 512
#define WORK_QUEUE_PRIORITY 12

#define TEST_PER_NO (10)

#define DESC_MAX_SIZE (20)
/*********************************
 *
 * copy from the linux core start
 *
 *********************************/
// this is the ip reg offset....don't change!!!!!!!
#define DW_DMA_MAX_NR_CHANNELS 8

/*
 * Redefine this macro to handle differences between 32- and 64-bit
 * addressing, big vs. little endian, etc.
 */
#define DW_REG(name)  \
    rt_uint32_t name; \
    rt_uint32_t __pad_##name

/* Hardware register definitions. */
struct dw_dma_chan_regs
{
    DW_REG(SAR);        /* Source Address Register */
    DW_REG(DAR);        /* Destination Address Register */
    DW_REG(LLP);        /* Linked List Pointer */
    rt_uint32_t CTL_LO; /* Control Register Low */
    rt_uint32_t CTL_HI; /* Control Register High */
    DW_REG(SSTAT);
    DW_REG(DSTAT);
    DW_REG(SSTATAR);
    DW_REG(DSTATAR);
    rt_uint32_t CFG_LO; /* Configuration Register Low */
    rt_uint32_t CFG_HI; /* Configuration Register High */
    DW_REG(SGR);
    DW_REG(DSR);
};

struct dw_dma_irq_regs
{
    DW_REG(XFER);
    DW_REG(BLOCK);
    DW_REG(SRC_TRAN);
    DW_REG(DST_TRAN);
    DW_REG(ERROR);
};

struct dw_dma_regs
{
    /* per-channel registers */
    struct dw_dma_chan_regs CHAN[DW_DMA_MAX_NR_CHANNELS];

    /* irq handling */
    struct dw_dma_irq_regs RAW;    /* r */
    struct dw_dma_irq_regs STATUS; /* r (raw & mask) */
    struct dw_dma_irq_regs MASK;   /* rw (set = irq enabled) */
    struct dw_dma_irq_regs CLEAR;  /* w (ack, affects "raw") */

    DW_REG(STATUS_INT); /* r */

    /* software handshaking */
    DW_REG(REQ_SRC);
    DW_REG(REQ_DST);
    DW_REG(SGL_REQ_SRC);
    DW_REG(SGL_REQ_DST);
    DW_REG(LAST_SRC);
    DW_REG(LAST_DST);

    /* miscellaneous */
    DW_REG(CFG);
    DW_REG(CH_EN);
    DW_REG(ID);
    DW_REG(TEST);

    /* optional encoded params, 0x3c8..0x3 */
};

/* Bitfields in CTL_LO */
#define DWC_CTLL_INT_EN (1 << 0)         /* irqs enabled? */
#define DWC_CTLL_DST_WIDTH(n) ((n) << 1) /* bytes per element */
#define DWC_CTLL_SRC_WIDTH(n) ((n) << 4)

#define DWC_CTLL_DST_INC_MODE(n) ((n) << 7)

#define DWC_CTLL_DST_INC (0 << 7) /* DAR update/not */
#define DWC_CTLL_DST_DEC (1 << 7)
#define DWC_CTLL_DST_FIX (2 << 7)

#define DWC_CTLL_SRC_INC_MODE(n) ((n) << 9)

#define DWC_CTLL_SRC_INC (0 << 9) /* SAR update/not */
#define DWC_CTLL_SRC_DEC (1 << 9)
#define DWC_CTLL_SRC_FIX (2 << 9)
#define DWC_CTLL_DST_MSIZE(n) ((n) << 11) /* burst, #elements */
#define DWC_CTLL_SRC_MSIZE(n) ((n) << 14)
#define DWC_CTLL_S_GATH_EN (1 << 17) /* src gather, !FIX */
#define DWC_CTLL_D_SCAT_EN (1 << 18) /* dst scatter, !FIX */
#define DWC_CTLL_FC(n) ((n) << 20)
#define DWC_CTLL_FC_M2M (0 << 20) /* mem-to-mem */
#define DWC_CTLL_FC_M2P (1 << 20) /* mem-to-periph */
#define DWC_CTLL_FC_P2M (2 << 20) /* periph-to-mem */
#define DWC_CTLL_FC_P2P (3 << 20) /* periph-to-periph */
/* plus 4 transfer types for peripheral-as-flow-controller */
#define DWC_CTLL_DMS(n) ((n) << 23) /* dst master select */
#define DWC_CTLL_SMS(n) ((n) << 25) /* src master select */
#define DWC_CTLL_LLP_D_EN (1 << 27) /* dest block chain */
#define DWC_CTLL_LLP_S_EN (1 << 28) /* src block chain */

/* Bitfields in CTL_HI */
#define DWC_CTLH_DONE 0x00001000
#define DWC_CTLH_BLOCK_TS_MASK 0x00000fff

/* Bitfields in CFG_LO. Platform-configurable bits are in <linux/dw_dmac.h> */
#define DWC_CFGL_CH_PRIOR_MASK (0x7 << 5) /* priority mask */
#define DWC_CFGL_CH_PRIOR(x) ((x) << 5)   /* priority */
#define DWC_CFGL_CH_SUSP (1 << 8)         /* pause xfer */
#define DWC_CFGL_FIFO_EMPTY (1 << 9)      /* pause xfer */

#define DWC_CFGL_HS_DST (1 << 10) /* handshake w/dst */
#define DWC_CFGL_HS_SRC (1 << 11) /* handshake w/src */
#define DWC_CFGL_MAX_BURST(x) ((x) << 20)
#define DWC_CFGL_RELOAD_SAR (1 << 30)
#define DWC_CFGL_RELOAD_DAR (1 << 31)

/* Bitfields in CFG_HI. Platform-configurable bits are in <linux/dw_dmac.h> */
#define DWC_CFGH_DS_UPD_EN (1 << 5)
#define DWC_CFGH_SS_UPD_EN (1 << 6)

/* Bitfields in SGR */
#define DWC_SGR_SGI(x) ((x) << 0)
#define DWC_SGR_SGC(x) ((x) << 20)

/* Bitfields in DSR */
#define DWC_DSR_DSI(x) ((x) << 0)
#define DWC_DSR_DSC(x) ((x) << 20)

/* Bitfields in CFG */
#define DW_CFG_DMA_EN (1 << 0)

#define DW_REGLEN 0x400

/* Platform-configurable bits in CFG_HI */
#define DWC_CFGH_FCMODE (1 << 0)
#define DWC_CFGH_FIFO_MODE (1 << 1)
#define DWC_CFGH_PROTCTL(x) ((x) << 2)
#define DWC_CFGH_SRC_PER(x) ((x) << 7)
#define DWC_CFGH_DST_PER(x) ((x) << 11)

/* Platform-configurable bits in CFG_LO */
#define DWC_CFGL_LOCK_CH_XFER (0 << 12) /* scope of LOCK_CH */
#define DWC_CFGL_LOCK_CH_BLOCK (1 << 12)
#define DWC_CFGL_LOCK_CH_XACT (2 << 12)
#define DWC_CFGL_LOCK_BUS_XFER (0 << 14) /* scope of LOCK_BUS */
#define DWC_CFGL_LOCK_BUS_BLOCK (1 << 14)
#define DWC_CFGL_LOCK_BUS_XACT (2 << 14)
#define DWC_CFGL_LOCK_CH (1 << 15)    /* channel lockout */
#define DWC_CFGL_LOCK_BUS (1 << 16)   /* busmaster lockout */
#define DWC_CFGL_HS_DST_POL (1 << 18) /* dst handshake active low */
#define DWC_CFGL_HS_SRC_POL (1 << 19) /* src handshake active low */

#define lift_shift_bit_num(bit_num) (1 << bit_num)

#define __raw_writeb(v, a) (*(volatile unsigned char *)(a) = (v))
#define __raw_writew(v, a) (*(volatile unsigned short *)(a) = (v))
#define __raw_writel(v, a) (*(volatile unsigned int *)(a) = (v))

#define __raw_readb(a) (*(volatile unsigned char *)(a))
#define __raw_readw(a) (*(volatile unsigned short *)(a))
#define __raw_readl(a) (*(volatile unsigned int *)(a))

#define min(a, b) (((a) < (b)) ? (a) : (b))
#define max(a, b) (((a) > (b)) ? (a) : (b))

#define dw_readl(dw, name) \
    __raw_readl(&(((struct dw_dma_regs *)dw->regs)->name))
#define dw_writel(dw, name, val) \
    __raw_writel((val), &(((struct dw_dma_regs *)dw->regs)->name))
#define dw_readw(dw, name) \
    __raw_readw(&(((struct dw_dma_regs *)dw->regs)->name))
#define dw_writew(dw, name, val) \
    __raw_writew((val), &(((struct dw_dma_regs *)dw->regs)->name))

#define CHANNEL0 (lift_shift_bit_num(0))
#define CHANNEL1 (lift_shift_bit_num(1))
#define CHANNEL2 (lift_shift_bit_num(2))
#define CHANNEL3 (lift_shift_bit_num(3))

#define channel_set_bit(dw, reg, mask) \
    dw_writel(dw, reg, ((mask) << 8) | (mask))
#define channel_clear_bit(dw, reg, mask) dw_writel(dw, reg, ((mask) << 8) | 0)

/****************************************************************************
 * ADT section
 *  add definition of user defined Data Type that only be used in this file here
 ***************************************************************************/

struct dw_dma
{
    // vadd
    void *regs;
    // padd
    rt_uint32_t paddr;
    rt_uint32_t irq;
    rt_uint32_t channel_max_number;

#define CONTROLLER_STATUS_CLOSED (0)
#define CONTROLLER_STATUS_OPEN (1)
    rt_uint32_t controller_status;
#define FH81_DMA_INIT_NOT_YET (0)
#define FH81_DMA_INIT_ALREADY (1)
    rt_uint32_t init;
    rt_uint32_t id;
    char *name;
    rt_uint32_t channel_work_done;
};

struct dma_channel
{
#define CHANNEL_STATUS_CLOSED (0)
#define CHANNEL_STATUS_OPEN (1)
#define CHANNEL_STATUS_IDLE (2)
#define CHANNEL_STATUS_BUSY (3)

    rt_uint32_t channel_status;  // open, busy ,closed
    rt_uint32_t desc_trans_size;

    // isr will set it complete.
    struct rt_completion transfer_completion;
    // add lock,when set the channel.lock it
    struct rt_semaphore channel_lock;
    // struct rt_mutex                 lock;
    // rt_enter_critical();
    rt_list_t queue;
    // active transfer now!!!
    struct dma_transfer *active_trans;

#define SINGLE_TRANSFER (0)
#define CYCLIC_TRANSFER (1)
#define DEFAULT_TRANSFER SINGLE_TRANSFER
    rt_uint32_t open_flag;
    //

    // new add para...
    rt_uint32_t desc_total_no;
    rt_uint32_t free_index;
    rt_uint32_t used_index;
    rt_uint32_t desc_left_cnt;

    rt_uint32_t allign_malloc;
    struct dw_lli *base_lli;
};

struct fh81_dma
{
    // core use ,this must be the first para!!!!
    struct rt_dma_device parent;
    // myown
    struct dw_dma dwc;
    // channel obj
    struct dma_channel dma_channel[FH81_MAX_CHANNEL];

    // struct rt_workqueue* isr_workqueue;
    // struct rt_work *isr_work;
};

#define list_for_each_entry_safe(pos, n, head, member)                \
    for (pos = rt_list_entry((head)->next, typeof(*pos), member),     \
        n    = rt_list_entry(pos->member.next, typeof(*pos), member); \
         &pos->member != (head);                                      \
         pos = n, n = rt_list_entry(n->member.next, typeof(*n), member))

/******************************************************************************
 * Function prototype section
 * add prototypes for all functions called by this file,execepting those
 * declared in header file
 *****************************************************************************/
void mmu_clean_invalidated_dcache(rt_uint32_t buffer, rt_uint32_t size);

/*****************************************************************************
 * Global variables section - Exported
 * add declaration of global variables that will be exported here
 * e.g.
 *  int8_t foo;
 ****************************************************************************/

/*****************************************************************************

 *  static fun;
 *****************************************************************************/
static rt_err_t init(struct rt_dma_device *dma);
static rt_err_t control(struct rt_dma_device *dma, int cmd, void *arg);

static void rt_fh_dma_cyclic_stop(struct dma_transfer *p);
static void rt_fh_dma_cyclic_start(struct dma_transfer *p);
static void rt_fh_dma_cyclic_prep(struct fh81_dma *fh81_dma_p,
                                  struct dma_transfer *p);
static void rt_fh_dma_cyclic_free(struct dma_transfer *p);
static void rt_fh_dma_cyclic_pause(struct dma_transfer *p);
static void rt_fh_dma_cyclic_resume(struct dma_transfer *p);

static struct rt_dma_ops fh81_dma_ops = {init, control};

/*****************************************************************************
 * Global variables section - Local
 * define global variables(will be refered only in this file) here,
 * static keyword should be used to limit scope of local variable to this file
 * e.g.
 *  static uint8_t ufoo;
 *****************************************************************************/
static struct fh81_dma fh81_dma_controller[DMA_CONTROLLER_NUMBER];

/* function body */
/*****************************************************************************
 * Description:
 *      add funtion description here
 * Parameters:
 *      description for each argument, new argument starts at new line
 * Return:
 *      what does this function returned?
 *****************************************************************************/

static rt_uint32_t allign_func(rt_uint32_t in_addr, rt_uint32_t allign_size)
{
    return (in_addr + allign_size - 1) & (~(allign_size - 1));
}

struct dw_lli *get_desc(struct fh81_dma *p_dma, struct dma_transfer *p_transfer,
                        rt_uint32_t lli_size)
{
    struct dw_lli *ret_lli;
    rt_uint32_t free_index;
    rt_uint32_t allign_left;
    rt_uint32_t totoal_desc;
    rt_uint32_t actual_get_desc = 0;
    rt_uint32_t totoal_free_desc;
    totoal_free_desc =
        p_dma->dma_channel[p_transfer->channel_number].desc_left_cnt;
    free_index  = p_dma->dma_channel[p_transfer->channel_number].free_index;
    totoal_desc = p_dma->dma_channel[p_transfer->channel_number].desc_total_no;
    allign_left = totoal_desc - free_index;

    // check first..
    if (totoal_free_desc < lli_size)
    {
        rt_kprintf("not enough desc to get...\n");
        rt_kprintf("get size is %d,left is %d\n", lli_size, totoal_free_desc);
        return RT_NULL;
    }
    // rt_kprintf("get desc in...\n");

    // rt_kprintf("lli size is %d\n",lli_size);
    if (lli_size > allign_left)
    {
        // if allign desc not enough...just reset null....
        if ((totoal_free_desc - allign_left) < lli_size)
        {
            rt_kprintf("not enough desc to get...\n");
            rt_kprintf(
                "app need size is %d, totoal left is %d, allign left is %d\n",
                lli_size, totoal_free_desc, allign_left);
            rt_kprintf("from head to get desc size is %d, actual get is %d\n",
                       (totoal_free_desc - allign_left),
                       (allign_left + lli_size));
            return RT_NULL;
        }
        else
        {
            actual_get_desc = allign_left + lli_size;
            free_index      = 0;
        }
    }

    // ret_lli =
    // &p_dma->dma_channel[p_transfer->channel_number].base_lli[free_index];

    ret_lli =
        &p_dma->dma_channel[p_transfer->channel_number].base_lli[free_index];
    //	rt_kprintf("get desc base index
    //addr:%08x\n",(rt_uint32_t)&p_dma->dma_channel[p_transfer->channel_number].base_lli[0]);
    //	rt_kprintf("get desc free index addr:%08x\n",(rt_uint32_t)ret_lli);
    //	rt_kprintf("get desc request size:%08x\n",lli_size);
    //	rt_kprintf("get desc total
    //size:%08x\n",p_dma->dma_channel[p_transfer->channel_number].desc_total_no);
    //	rt_kprintf("one desc size is:%08x\n",sizeof(	struct dw_lli));

    p_dma->dma_channel[p_transfer->channel_number].free_index +=
        actual_get_desc;

    // rt_kprintf("get desc free index
    // addr:%08x\n",(rt_uint32_t)&p_dma->dma_channel[p_transfer->channel_number].base_lli[p_dma->dma_channel[p_transfer->channel_number].free_index]);

    p_dma->dma_channel[p_transfer->channel_number].free_index %=
        p_dma->dma_channel[p_transfer->channel_number].desc_total_no;
    p_dma->dma_channel[p_transfer->channel_number].desc_left_cnt -=
        actual_get_desc;
    p_transfer->lli_size        = lli_size;
    p_transfer->actual_lli_size = actual_get_desc;
    return ret_lli;
}

rt_uint32_t put_desc(struct fh81_dma *p_dma, struct dma_transfer *p_transfer)
{
//    rt_uint32_t used_index;
    rt_uint32_t lli_size;
    // rt_kprintf("put desc in...\n");
//    used_index = p_dma->dma_channel[p_transfer->channel_number].used_index;
    lli_size   = p_transfer->actual_lli_size;
    p_dma->dma_channel[p_transfer->channel_number].used_index += lli_size;
    p_dma->dma_channel[p_transfer->channel_number].used_index %=
        p_dma->dma_channel[p_transfer->channel_number].desc_total_no;
    p_dma->dma_channel[p_transfer->channel_number].desc_left_cnt += lli_size;
    p_transfer->lli_size        = 0;
    p_transfer->actual_lli_size = 0;
    return 0;
}

/*****************************************************************************
 * Description:
 *      add funtion description here
 * Parameters:
 *      description for each argument, new argument starts at new line
 * Return:
 *      what does this function returned?
 *****************************************************************************/

static rt_err_t init(struct rt_dma_device *dma)
{
    // init the clk table

    struct fh81_dma *my_own RT_UNUSED ;
    my_own = (struct fh81_dma *)dma->parent.user_data;

    FH_DMA_DEBUG("my_own value:0x%x\n", (rt_uint32_t)my_own);

    // check the user data
    RT_ASSERT(my_own != RT_NULL);

    return RT_EOK;
}

/*****************************************************************************
 * Description:
 *      add funtion description here
 * Parameters:
 *      description for each argument, new argument starts at new line
 * Return:
 *      what does this function returned?
 *****************************************************************************/

static void handle_dma_open(struct fh81_dma *p_dma)
{
    struct dw_dma *temp_dwc;
    temp_dwc = &p_dma->dwc;

    dw_writel(temp_dwc, CFG, 1);
    p_dma->dwc.controller_status = CONTROLLER_STATUS_OPEN;
}

/*****************************************************************************
 * Description:
 *      add funtion description here
 * Parameters:
 *      description for each argument, new argument starts at new line
 * Return:
 *      what does this function returned?
 *****************************************************************************/
static void handle_dma_close(struct fh81_dma *p_dma)
{
    rt_uint32_t i;
    struct dw_dma *temp_dwc;
    temp_dwc = &p_dma->dwc;

    // take lock
    for (i = 0; i < p_dma->dwc.channel_max_number; i++)
    {
        rt_sem_take(&p_dma->dma_channel[i].channel_lock, RT_WAITING_FOREVER);

        channel_clear_bit(temp_dwc, CH_EN, lift_shift_bit_num(i));
        p_dma->dma_channel[i].channel_status = CHANNEL_STATUS_CLOSED;
    }
    dw_writel(temp_dwc, CFG, 0);
    p_dma->dwc.controller_status = CONTROLLER_STATUS_CLOSED;

    // release lock
    for (i = 0; i < p_dma->dwc.channel_max_number; i++)
    {
        rt_sem_release(&p_dma->dma_channel[i].channel_lock);
    }

    // destroy the workqueue..
    // rt_workqueue_destroy(p_dma->isr_workqueue);
}

/*****************************************************************************
 * Description:
 *      add funtion description here
 * Parameters:
 *      description for each argument, new argument starts at new line
 * Return:
 *      what does this function returned?
 *****************************************************************************/

#define CHANNEL_REAL_FREE (0)
#define CHANNEL_NOT_FREE (1)

static rt_uint32_t check_channel_real_free(struct fh81_dma *p_dma,
                                           rt_uint32_t channel_number)
{
    struct dw_dma *temp_dwc;
    temp_dwc = &p_dma->dwc;
    rt_uint32_t ret_status;

    RT_ASSERT(channel_number < p_dma->dwc.channel_max_number);

    ret_status = dw_readl(temp_dwc, CH_EN);
    if (ret_status & lift_shift_bit_num(channel_number))
    {
        // the channel is still busy!!!error here
        // FH_DMA_DEBUG("auto request channel error\n");
        return CHANNEL_NOT_FREE;
    }
    return CHANNEL_REAL_FREE;
}

/*****************************************************************************
 * Description:
 *      add funtion description here
 * Parameters:
 *      description for each argument, new argument starts at new line
 * Return:
 *      what does this function returned?
 *****************************************************************************/

static rt_err_t handle_request_channel(struct fh81_dma *p_dma,
                                       struct dma_transfer *p_transfer)
{
    rt_uint32_t i;
    rt_err_t ret_status = RT_EOK;

    // handle if auto check channel...
    if (p_transfer->channel_number == AUTO_FIND_CHANNEL)
    {
        // check each channel lock,find a free channel...
        for (i = 0; i < p_dma->dwc.channel_max_number; i++)
        {
            ret_status = rt_sem_trytake(&p_dma->dma_channel[i].channel_lock);
            if (ret_status == RT_EOK)
            {
                break;
            }
        }

        if (i < p_dma->dwc.channel_max_number)
        {
            ret_status = check_channel_real_free(p_dma, i);
            if (ret_status != CHANNEL_REAL_FREE)
            {
                FH_DMA_DEBUG("auto request channel error\n");
                RT_ASSERT(ret_status == CHANNEL_REAL_FREE);
            }
            // caution : channel is already locked here....
            p_transfer->channel_number = i;
            // bind to the controller.
            // p_transfer->dma_controller = p_dma;
            p_dma->dma_channel[i].channel_status = CHANNEL_STATUS_OPEN;
        }
        else
            return -RT_ENOMEM;
    }

    // request channel by user
    else
    {
        //

        RT_ASSERT(p_transfer->channel_number < p_dma->dwc.channel_max_number);
        ret_status = rt_sem_take(
            &p_dma->dma_channel[p_transfer->channel_number].channel_lock,
            RT_TICK_PER_SECOND * 50);
        if (ret_status != RT_EOK) return -RT_ENOMEM;
        // rt_enter_critical();
        ret_status = check_channel_real_free(p_dma, p_transfer->channel_number);
        if (ret_status != CHANNEL_REAL_FREE)
        {
            FH_DMA_DEBUG("user request channel error\n");
            RT_ASSERT(ret_status == CHANNEL_REAL_FREE);
        }

        // bind to the controller
        // p_transfer->dma_controller = p_dma;
        p_dma->dma_channel[p_transfer->channel_number].channel_status =
            CHANNEL_STATUS_OPEN;
        // rt_exit_critical();
    }

    // malloc desc for this one channel...
    // fix me....

    p_dma->dma_channel[p_transfer->channel_number].allign_malloc =
        (rt_uint32_t)rt_malloc(
            (p_dma->dma_channel[p_transfer->channel_number].desc_total_no *
             sizeof(struct dw_lli)) +
            CACHE_LINE_SIZE);

    if (!p_dma->dma_channel[p_transfer->channel_number].allign_malloc)
    {
        // release channel
        rt_kprintf("[dma]: no mem to malloc channel%d desc..\n",
                   p_transfer->channel_number);
        p_dma->dma_channel[p_transfer->channel_number].channel_status =
            CHANNEL_STATUS_CLOSED;
        rt_sem_release(
            &p_dma->dma_channel[p_transfer->channel_number].channel_lock);
        return -RT_ENOMEM;
    }

    p_dma->dma_channel[p_transfer->channel_number].base_lli =
        (struct dw_lli *)allign_func(
            p_dma->dma_channel[p_transfer->channel_number].allign_malloc,
            CACHE_LINE_SIZE);

    FH_DMA_DEBUG(
        "dma desc addr is %x\n",
        (rt_uint32_t)p_dma->dma_channel[p_transfer->channel_number].base_lli);
    // t1 = (UINT32)rt_malloc(GMAC_TX_RING_SIZE *
    // sizeof(Gmac_Tx_DMA_Descriptors) + CACHE_LINE_SIZE);

    if (!p_dma->dma_channel[p_transfer->channel_number].base_lli)
    {
        FH_DMA_DEBUG("request desc failed..\n");
        RT_ASSERT(p_dma->dma_channel[p_transfer->channel_number].base_lli !=
                  RT_NULL);
    }

    if ((rt_uint32_t)p_dma->dma_channel[p_transfer->channel_number].base_lli %
        32)
    {
        rt_kprintf("malloc is not cache allign..");
    }

    // rt_memset((void *)dma_trans_desc->first_lli, 0, lli_size * sizeof(struct
    // dw_lli));
    rt_memset((void *)p_dma->dma_channel[p_transfer->channel_number].base_lli,
              0, p_dma->dma_channel[p_transfer->channel_number].desc_total_no *
                     sizeof(struct dw_lli));

    p_dma->dma_channel[p_transfer->channel_number].desc_left_cnt =
        p_dma->dma_channel[p_transfer->channel_number].desc_total_no;
    p_dma->dma_channel[p_transfer->channel_number].free_index = 0;
    p_dma->dma_channel[p_transfer->channel_number].used_index = 0;

    return RT_EOK;
}

/*****************************************************************************
 * Description:
 *      add funtion description here
 * Parameters:
 *      description for each argument, new argument starts at new line
 * Return:
 *      what does this function returned?
 *****************************************************************************/

static rt_uint32_t handle_release_channel(struct fh81_dma *p_dma,
                                          struct dma_transfer *p_transfer)
{
    struct dw_dma *temp_dwc;
    temp_dwc = &p_dma->dwc;
    rt_uint32_t ret_status;

    // rt_enter_critical();
    ret_status = p_dma->dma_channel[p_transfer->channel_number].channel_status;

    RT_ASSERT(p_transfer->channel_number < p_dma->dwc.channel_max_number);

    if (ret_status == CHANNEL_STATUS_CLOSED)
    {
        FH_DMA_DEBUG(
            "release channel error,reason: release a closed channel!!\n");
        RT_ASSERT(ret_status != CHANNEL_STATUS_CLOSED);
    }

    channel_clear_bit(temp_dwc, CH_EN,
                      lift_shift_bit_num(p_transfer->channel_number));
    rt_sem_release(
        &p_dma->dma_channel[p_transfer->channel_number].channel_lock);
    // p_transfer->dma_controller = RT_NULL;
    p_dma->dma_channel[p_transfer->channel_number].channel_status =
        CHANNEL_STATUS_CLOSED;
    p_dma->dma_channel[p_transfer->channel_number].open_flag = DEFAULT_TRANSFER;
    // rt_exit_critical();

    // release this channel malloc mem...
    // fix me.....
    rt_free(
        (void *)p_dma->dma_channel[p_transfer->channel_number].allign_malloc);
    p_dma->dma_channel[p_transfer->channel_number].allign_malloc = RT_NULL;
    p_dma->dma_channel[p_transfer->channel_number].base_lli      = RT_NULL;
    p_dma->dma_channel[p_transfer->channel_number].desc_left_cnt =
        p_dma->dma_channel[p_transfer->channel_number].desc_total_no;
    p_dma->dma_channel[p_transfer->channel_number].free_index = 0;
    p_dma->dma_channel[p_transfer->channel_number].used_index = 0;

    return RT_EOK;
}

static rt_uint32_t cal_lli_size(struct dma_transfer *p_transfer)
{
    RT_ASSERT(p_transfer != RT_NULL);
    RT_ASSERT(p_transfer->dma_controller != RT_NULL);
    RT_ASSERT(p_transfer->src_width <= DW_DMA_SLAVE_WIDTH_32BIT);
    rt_uint32_t lli_number                = 0;
    rt_uint32_t channel_max_trans_per_lli = 0;
    channel_max_trans_per_lli =
        p_transfer->dma_controller->dma_channel[p_transfer->channel_number]
            .desc_trans_size;

    lli_number = (p_transfer->trans_len % channel_max_trans_per_lli) ? 1 : 0;
    lli_number += p_transfer->trans_len / channel_max_trans_per_lli;

    return lli_number;
}

static void dump_lli(struct dw_lli *p_lli)
{
    FH_DMA_DEBUG(
        "link_mem padd:0x%x\n sar:0x%x\n dar:0x%x\n llp:0x%x\n ctllo:0x%x\n "
        "ctlhi:0x%x\n sstat:0x%x\n dstat:0x%x\n",
        (rt_uint32_t)p_lli, p_lli->sar, p_lli->dar, p_lli->llp, p_lli->ctllo,
        p_lli->ctlhi, p_lli->sstat, p_lli->dstat);
}
/*****************************************************************************
 * Description:
 *      add funtion description here
 * Parameters:
 *      description for each argument, new argument starts at new line
 * Return:
 *      what does this function returned?
 *****************************************************************************/
static void handle_single_transfer(struct fh81_dma *p_dma,
                                   struct dma_transfer *p_transfer)
{
    rt_uint32_t i;
    struct dw_dma *temp_dwc;
    temp_dwc = &p_dma->dwc;
    volatile rt_uint32_t ret_status;
    rt_list_t *p_controller_list;
    rt_uint32_t lli_size, max_trans_size;
    struct dw_lli *p_lli = RT_NULL;
    struct dma_transfer *dma_trans_desc;
    struct dma_transfer *_dma_trans_desc;

    rt_uint32_t temp_src_add = 0;
    rt_uint32_t temp_dst_add = 0;
    rt_uint32_t trans_total_len = 0;
    rt_uint32_t temp_trans_size = 0;
    // rt_uint32_t dma_channl_no = 0;

    RT_ASSERT(p_transfer->channel_number < p_dma->dwc.channel_max_number);
    RT_ASSERT(p_transfer->dma_number < DMA_CONTROLLER_NUMBER);
    RT_ASSERT(&fh81_dma_controller[p_transfer->dma_number] == p_dma);
    // when the dma transfer....the lock should be 0!!!!
    // or user may not request the channel...
    RT_ASSERT(
        p_dma->dma_channel[p_transfer->channel_number].channel_lock.value == 0);

    ret_status = p_dma->dma_channel[p_transfer->channel_number].channel_status;
    if (ret_status == CHANNEL_STATUS_CLOSED)
    {
        FH_DMA_DEBUG("transfer error,reason: use a closed channel..\n");
        RT_ASSERT(ret_status != CHANNEL_STATUS_CLOSED);
    }
    p_transfer->dma_controller = p_dma;

    rt_list_init(&p_transfer->transfer_list);
    max_trans_size =
        p_transfer->dma_controller->dma_channel[p_transfer->channel_number]
            .desc_trans_size;
    // add transfer to the controller's queue list
    // here should  insert before and handle after....this could be a fifo...
    rt_list_insert_before(&p_dma->dma_channel[p_transfer->channel_number].queue,
                          &p_transfer->transfer_list);

    p_controller_list = &p_dma->dma_channel[p_transfer->channel_number].queue;

    // here the driver could make a queue to cache the transfer and kick a
    // thread to handle the queue~~~
    // but now,this is a easy version...,just handle the transfer now!!!
    list_for_each_entry_safe(dma_trans_desc, _dma_trans_desc, p_controller_list,
                             transfer_list)
    {
        // the dma controller could see the active transfer .....
        p_transfer->dma_controller->dma_channel[p_transfer->channel_number]
            .active_trans = dma_trans_desc;

        trans_total_len = p_transfer->trans_len;

        // handle desc
        // step1:cal lli size...
        lli_size = cal_lli_size(dma_trans_desc);
        // step2:malloc lli_size  mem
        // dma_trans_desc->first_lli = (struct dw_lli *)rt_malloc(lli_size *
        // sizeof(struct dw_lli));

        dma_trans_desc->first_lli = get_desc(p_dma, p_transfer, lli_size);

        // not enough mem..
        if (dma_trans_desc->first_lli == RT_NULL)
        {
            FH_DMA_DEBUG("transfer error,reason: not enough mem..\n");
            RT_ASSERT(dma_trans_desc->first_lli != RT_NULL);
        }

        // bug here....
        rt_memset((void *)dma_trans_desc->first_lli, 0,
                  lli_size * sizeof(struct dw_lli));

        p_lli = dma_trans_desc->first_lli;

        // warnning!!!!must check if the add is 32bits ally...
        RT_ASSERT(((rt_uint32_t)p_lli & 0x03) == 0);

        RT_ASSERT(dma_trans_desc->dst_inc_mode <= DW_DMA_SLAVE_FIX);
        RT_ASSERT(dma_trans_desc->src_inc_mode <= DW_DMA_SLAVE_FIX);
        // step3: set the mem..
        for (i = 0; i < lli_size; i++)
        {
            // parse trans para...
            // para add:

            switch (dma_trans_desc->dst_inc_mode)
            {
            case DW_DMA_SLAVE_INC:
                temp_dst_add =
                    dma_trans_desc->dst_add +
                    i * max_trans_size * (1 << dma_trans_desc->dst_width);
                break;
            case DW_DMA_SLAVE_DEC:
                temp_dst_add =
                    dma_trans_desc->dst_add -
                    i * max_trans_size * (1 << dma_trans_desc->dst_width);
                break;
            case DW_DMA_SLAVE_FIX:
                temp_dst_add = dma_trans_desc->dst_add;
                break;
            }

            switch (dma_trans_desc->src_inc_mode)
            {
            case DW_DMA_SLAVE_INC:
                temp_src_add =
                    dma_trans_desc->src_add +
                    i * max_trans_size * (1 << dma_trans_desc->src_width);
                break;
            case DW_DMA_SLAVE_DEC:
                temp_src_add =
                    dma_trans_desc->src_add -
                    i * max_trans_size * (1 << dma_trans_desc->src_width);
                break;
            case DW_DMA_SLAVE_FIX:
                temp_src_add = dma_trans_desc->src_add;
                break;
            }

            p_lli[i].sar = temp_src_add;
            p_lli[i].dar = temp_dst_add;

            // para ctl
            temp_trans_size = (trans_total_len / max_trans_size)
                                  ? max_trans_size
                                  : (trans_total_len % max_trans_size);
            trans_total_len -= temp_trans_size;

            RT_ASSERT(dma_trans_desc->dst_width <= DW_DMA_SLAVE_WIDTH_32BIT);
            RT_ASSERT(dma_trans_desc->src_width <= DW_DMA_SLAVE_WIDTH_32BIT);

            RT_ASSERT(dma_trans_desc->dst_msize <= DW_DMA_SLAVE_MSIZE_256);
            RT_ASSERT(dma_trans_desc->src_msize <= DW_DMA_SLAVE_MSIZE_256);
            RT_ASSERT(dma_trans_desc->fc_mode <= DMA_P2P);

            p_lli[i].ctllo =
                DWC_CTLL_INT_EN |
                DWC_CTLL_DST_WIDTH(dma_trans_desc->dst_width) |
                DWC_CTLL_SRC_WIDTH(dma_trans_desc->src_width) |
                DWC_CTLL_DST_INC_MODE(dma_trans_desc->dst_inc_mode) |
                DWC_CTLL_SRC_INC_MODE(dma_trans_desc->src_inc_mode) |
                DWC_CTLL_DST_MSIZE(dma_trans_desc->dst_msize) |
                DWC_CTLL_SRC_MSIZE(dma_trans_desc->src_msize) |
                DWC_CTLL_FC(dma_trans_desc->fc_mode) | DWC_CTLL_DMS(0) |
                DWC_CTLL_SMS(0);
            // block size
            p_lli[i].ctlhi = temp_trans_size;

            if (trans_total_len > 0)
            {
                p_lli[i].llp = (rt_uint32_t)&p_lli[i + 1];
                p_lli[i].ctllo |= DWC_CTLL_LLP_D_EN | DWC_CTLL_LLP_S_EN;
            }

            // flush cache to mem
            mmu_clean_invalidated_dcache((rt_uint32_t)&p_lli[i],
                                         sizeof(struct dw_lli));

            dump_lli(&p_lli[i]);
        }

        // clear the isr status

        // set the dma config reg
        // clear cfg reload reg
        // ret_status =
        // dw_readl(temp_dwc,CHAN[dma_trans_desc->channel_number].CFG_LO);
        // ret_status &= ~(DWC_CFGL_RELOAD_SAR|DWC_CFGL_RELOAD_DAR);
        dw_writel(temp_dwc, CHAN[dma_trans_desc->channel_number].CFG_LO, 0);

        // set the first link add
        // ret_status =
        // dw_readl(temp_dwc,CHAN[dma_trans_desc->channel_number].LLP);
        ret_status = 0;
        ret_status = (rt_uint32_t)&p_lli[0];
        dw_writel(temp_dwc, CHAN[dma_trans_desc->channel_number].LLP,
                  ret_status);

        // set link enable
        // ret_status =
        // dw_readl(temp_dwc,CHAN[dma_trans_desc->channel_number].CTL_LO);
        ret_status = 0;
        ret_status = DWC_CTLL_LLP_D_EN | DWC_CTLL_LLP_S_EN;
        dw_writel(temp_dwc, CHAN[dma_trans_desc->channel_number].CTL_LO,
                  ret_status);

        dw_writel(temp_dwc, CHAN[dma_trans_desc->channel_number].CTL_HI, 0);
        // set handshaking

        RT_ASSERT(dma_trans_desc->dst_hs <= DMA_SW_HANDSHAKING);
        RT_ASSERT(dma_trans_desc->src_hs <= DMA_SW_HANDSHAKING);

        if (dma_trans_desc->dst_hs == DMA_SW_HANDSHAKING)
        {
            ret_status =
                dw_readl(temp_dwc, CHAN[dma_trans_desc->channel_number].CFG_LO);
            ret_status |= DWC_CFGL_HS_DST;
            dw_writel(temp_dwc, CHAN[dma_trans_desc->channel_number].CFG_LO,
                      ret_status);
        }
        else
        {
            ret_status =
                dw_readl(temp_dwc, CHAN[dma_trans_desc->channel_number].CFG_LO);
            ret_status &= ~DWC_CFGL_HS_DST;
            dw_writel(temp_dwc, CHAN[dma_trans_desc->channel_number].CFG_LO,
                      ret_status);
        }

        if (dma_trans_desc->src_hs == DMA_SW_HANDSHAKING)
        {
            ret_status =
                dw_readl(temp_dwc, CHAN[dma_trans_desc->channel_number].CFG_LO);
            ret_status |= DWC_CFGL_HS_SRC;
            dw_writel(temp_dwc, CHAN[dma_trans_desc->channel_number].CFG_LO,
                      ret_status);
        }
        else
        {
            ret_status =
                dw_readl(temp_dwc, CHAN[dma_trans_desc->channel_number].CFG_LO);
            ret_status &= ~DWC_CFGL_HS_SRC;
            dw_writel(temp_dwc, CHAN[dma_trans_desc->channel_number].CFG_LO,
                      ret_status);
        }

        // only hw handshaking need this..
        switch (dma_trans_desc->fc_mode)
        {
        case DMA_M2M:
            break;
        case DMA_M2P:
            // set dst per...
            RT_ASSERT(dma_trans_desc->dst_per < DMA_HW_HS_END);

            ret_status =
                dw_readl(temp_dwc, CHAN[dma_trans_desc->channel_number].CFG_HI);

            // clear 43 ~ 46 bit
            ret_status &= ~0x7800;

            ret_status |= DWC_CFGH_DST_PER(dma_trans_desc->dst_per);
            dw_writel(temp_dwc, CHAN[dma_trans_desc->channel_number].CFG_HI,
                      ret_status);
            // DWC_CFGH_SRC_PER

            break;
        case DMA_P2M:
            // set src per...
            RT_ASSERT(dma_trans_desc->src_per < DMA_HW_HS_END);

            ret_status =
                dw_readl(temp_dwc, CHAN[dma_trans_desc->channel_number].CFG_HI);

            // clear 39 ~ 42 bit
            ret_status &= ~0x780;

            ret_status |= DWC_CFGH_SRC_PER(dma_trans_desc->src_per);
            dw_writel(temp_dwc, CHAN[dma_trans_desc->channel_number].CFG_HI,
                      ret_status);

            break;
        case DMA_P2P:
            // set src and dst..
            RT_ASSERT(dma_trans_desc->dst_per < DMA_HW_HS_END);
            RT_ASSERT(dma_trans_desc->src_per < DMA_HW_HS_END);

            ret_status =
                dw_readl(temp_dwc, CHAN[dma_trans_desc->channel_number].CFG_HI);
            ret_status &= ~0x7800;
            ret_status &= ~0x780;
            ret_status |= DWC_CFGH_SRC_PER(dma_trans_desc->src_per) |
                          DWC_CFGH_DST_PER(dma_trans_desc->dst_per);
            dw_writel(temp_dwc, CHAN[dma_trans_desc->channel_number].CFG_HI,
                      ret_status);

            break;
        default:
            break;
        }

        dma_trans_desc->dma_controller
            ->dma_channel[dma_trans_desc->channel_number]
            .channel_status = CHANNEL_STATUS_BUSY;
        // enable isr...
        channel_set_bit(temp_dwc, MASK.XFER,
                        lift_shift_bit_num(dma_trans_desc->channel_number));
        channel_set_bit(temp_dwc, MASK.ERROR,
                        lift_shift_bit_num(dma_trans_desc->channel_number));
        // close
        channel_clear_bit(temp_dwc, MASK.BLOCK,
                          lift_shift_bit_num(dma_trans_desc->channel_number));

        dw_writel(temp_dwc, CLEAR.XFER, 1 << (dma_trans_desc->channel_number));
        dw_writel(temp_dwc, CLEAR.BLOCK, 1 << (dma_trans_desc->channel_number));
        dw_writel(temp_dwc, CLEAR.SRC_TRAN,
                  1 << (dma_trans_desc->channel_number));
        dw_writel(temp_dwc, CLEAR.DST_TRAN,
                  1 << (dma_trans_desc->channel_number));
        dw_writel(temp_dwc, CLEAR.ERROR, 1 << (dma_trans_desc->channel_number));

        ret_status =
            dw_readl(temp_dwc, CHAN[dma_trans_desc->channel_number].CFG_HI);
        FH_DMA_DEBUG("cfg_hi value:0x%x\n", ret_status);

        ret_status =
            dw_readl(temp_dwc, CHAN[dma_trans_desc->channel_number].CFG_LO);
        FH_DMA_DEBUG("cfg_low value:0x%x\n", ret_status);

        ret_status = dw_readl(temp_dwc, MASK.BLOCK);
        FH_DMA_DEBUG("mask block value:0x%x\n", ret_status);

        ret_status = dw_readl(temp_dwc, MASK.XFER);
        FH_DMA_DEBUG("mask xfer value:0x%x\n", ret_status);

        if (dma_trans_desc->prepare_callback)
        {
            dma_trans_desc->prepare_callback(dma_trans_desc->prepare_para);
        }
        // enable the channle to transfer
        channel_set_bit(temp_dwc, CH_EN,
                        lift_shift_bit_num(dma_trans_desc->channel_number));
    }
}

/*****************************************************************************
 * Description:
 *      add funtion description here
 * Parameters:
 *      description for each argument, new argument starts at new line
 * Return:
 *      what does this function returned?
 *****************************************************************************/
static rt_err_t control(struct rt_dma_device *dma, int cmd, void *arg)
{
    struct fh81_dma *my_own = (struct fh81_dma *)dma->parent.user_data;
    struct dw_dma *dwc RT_UNUSED;
    dwc = &my_own->dwc;

    rt_err_t ret = RT_EOK;

    struct dma_transfer *p_dma_transfer = (struct dma_transfer *)arg;

    // FH_DMA_DEBUG("p_dma_transfer value:0x%x\n",(rt_uint32_t)p_dma_transfer);

    RT_ASSERT(my_own != RT_NULL);
    RT_ASSERT(dwc != RT_NULL);

    switch (cmd)
    {
    case RT_DEVICE_CTRL_DMA_OPEN:

        // open the controller..
        handle_dma_open(my_own);
        break;
    case RT_DEVICE_CTRL_DMA_CLOSE:

        // close the controller..
        handle_dma_close(my_own);
        break;
    case RT_DEVICE_CTRL_DMA_REQUEST_CHANNEL:
        // request a channel for the user
        RT_ASSERT(p_dma_transfer != RT_NULL);
        ret = handle_request_channel(my_own, p_dma_transfer);

        break;
    case RT_DEVICE_CTRL_DMA_RELEASE_CHANNEL:
        // release a channel
        RT_ASSERT(p_dma_transfer != RT_NULL);

        ret = handle_release_channel(my_own, p_dma_transfer);

        break;

    case RT_DEVICE_CTRL_DMA_SINGLE_TRANSFER:
        // make a channel to transfer data.
        RT_ASSERT(p_dma_transfer != RT_NULL);
        // check if the dma channel is open,or return error.

        my_own->dma_channel[p_dma_transfer->channel_number].open_flag =
            SINGLE_TRANSFER;
        handle_single_transfer(my_own, p_dma_transfer);
        // then wait for the channel is complete..
        // caution that::we should be in the "rt_enter_critical()"when set the
        // dma to work.
        break;

    case RT_DEVICE_CTRL_DMA_CYCLIC_PREPARE:
        RT_ASSERT(p_dma_transfer != RT_NULL);
        my_own->dma_channel[p_dma_transfer->channel_number].open_flag =
            CYCLIC_TRANSFER;
        rt_fh_dma_cyclic_prep(my_own, p_dma_transfer);
        break;

    case RT_DEVICE_CTRL_DMA_CYCLIC_START:
        rt_fh_dma_cyclic_start(p_dma_transfer);
        break;

    case RT_DEVICE_CTRL_DMA_CYCLIC_STOP:
        rt_fh_dma_cyclic_stop(p_dma_transfer);
        break;

    case RT_DEVICE_CTRL_DMA_CYCLIC_FREE:
        rt_fh_dma_cyclic_free(p_dma_transfer);
        break;
    case RT_DEVICE_CTRL_DMA_PAUSE:
        rt_fh_dma_cyclic_pause(p_dma_transfer);
        break;
    case RT_DEVICE_CTRL_DMA_RESUME:
        rt_fh_dma_cyclic_resume(p_dma_transfer);
        break;

    default:
        break;
    }

    return ret;
}

static void rt_fh81_dma_isr(int irq, void *param)
{
    RT_ASSERT(irq == DMAC_IRQn);
    rt_uint32_t isr_channel_x, i, error, isr_channel_b;
    struct fh81_dma *my_own = (struct fh81_dma *)param;
    struct dw_dma *dwc;
    struct dma_transfer *p_transfer;
    dwc = &my_own->dwc;
    // p_transfer =
    // rt_kprintf("dma isr get in~~~\n");
    error = dw_readl(dwc, STATUS.ERROR);
    if (error != 0)
    {
        FH_DMA_DEBUG("dma isr error!!!!\n");
        RT_ASSERT(error == RT_NULL);
    }

    isr_channel_x = dw_readl(dwc, STATUS.XFER);
    isr_channel_b = dw_readl(dwc, STATUS.BLOCK);
    // for single check the transfer status
    // check which channel...

    for (i = 0; i < my_own->dwc.channel_max_number; i++)
    {
        if (my_own->dma_channel[i].open_flag == SINGLE_TRANSFER)
        {
            if (isr_channel_x & 1 << i)
            {
                dw_writel(dwc, CLEAR.XFER, 1 << i);

                p_transfer = my_own->dma_channel[i].active_trans;

                if (p_transfer->complete_callback)
                {
                    p_transfer->complete_callback(p_transfer->complete_para);
                }
                p_transfer->dma_controller
                    ->dma_channel[p_transfer->channel_number]
                    .channel_status = CHANNEL_STATUS_IDLE;
                // here is a bug...do not free here
                // rt_free(p_transfer->first_lli);
                put_desc(my_own, p_transfer);
                rt_list_remove(&p_transfer->transfer_list);
            }
        }

        else if (my_own->dma_channel[i].open_flag == CYCLIC_TRANSFER)
        {
            if (isr_channel_b & 1 << i)
            {
                p_transfer = my_own->dma_channel[i].active_trans;
                dw_writel(dwc, CLEAR.BLOCK, 1 << (p_transfer->channel_number));
                if (p_transfer->complete_callback)
                {
                    p_transfer->complete_callback(p_transfer->complete_para);
                }
            }
        }
    }
}

/*****************************************************************************
 * Description:
 *      add funtion description here
 * Parameters:
 *      description for each argument, new argument starts at new line
 * Return:
 *      what does this function returned?
 *****************************************************************************/

const char *channel_lock_name[FH81_MAX_CHANNEL] = {
    "channel_0_lock", "channel_1_lock", "channel_2_lock", "channel_3_lock",
};

rt_err_t fh81_dma_register(struct fh81_dma *fh81_dma_p, char *dma_name)
{
    rt_uint32_t i;

    RT_ASSERT(fh81_dma_p != RT_NULL);
    RT_ASSERT(dma_name != RT_NULL);
    // RT_ASSERT(fh81_dma_p->dwc.init != FH81_DMA_INIT_ALREADY);

    if (fh81_dma_p->dwc.init == FH81_DMA_INIT_ALREADY) return 0;

    struct rt_dma_device *rt_dma;
    rt_dma      = &fh81_dma_p->parent;
    rt_dma->ops = &fh81_dma_ops;

    // soc para set
    fh81_dma_p->dwc.name               = dma_name;
    fh81_dma_p->dwc.regs               = (void *)DMA_REG_BASE;
    fh81_dma_p->dwc.paddr              = DMA_REG_BASE;
    fh81_dma_p->dwc.irq                = DMAC_IRQn;
    fh81_dma_p->dwc.channel_max_number = FH81_MAX_CHANNEL;
    fh81_dma_p->dwc.controller_status  = CONTROLLER_STATUS_CLOSED;
    fh81_dma_p->dwc.init               = FH81_DMA_INIT_ALREADY;
    fh81_dma_p->dwc.id                 = 0;
    // channel set
    for (i = 0; i < FH81_MAX_CHANNEL; i++)
    {
        fh81_dma_p->dma_channel[i].channel_status = CHANNEL_STATUS_CLOSED;
        fh81_dma_p->dma_channel[i].desc_total_no  = DESC_MAX_SIZE;
        // rt_completion_init(&(fh81_dma_p->dma_channel[i].transfer_completion));
        rt_list_init(&(fh81_dma_p->dma_channel[i].queue));
        fh81_dma_p->dma_channel[i].desc_trans_size =
            FH81_CHANNEL_MAX_TRANSFER_SIZE;
        rt_sem_init(&fh81_dma_p->dma_channel[i].channel_lock,
                    channel_lock_name[i], 1, RT_IPC_FLAG_FIFO);
    }

    // isr
    rt_hw_interrupt_install(fh81_dma_p->dwc.irq, rt_fh81_dma_isr,
                            (void *)fh81_dma_p, "dma_isr");
    rt_hw_interrupt_umask(fh81_dma_p->dwc.irq);

    return rt_hw_dma_register(rt_dma, dma_name, RT_DEVICE_FLAG_RDWR,
                              fh81_dma_p);
}

static void rt_fh_dma_cyclic_pause(struct dma_transfer *p)
{
    return rt_fh_dma_cyclic_stop(p);
}

static void rt_fh_dma_cyclic_resume(struct dma_transfer *p)
{
    struct fh81_dma *my_own = p->dma_controller;
    struct dw_dma *dwc;
    dwc = &my_own->dwc;
    channel_set_bit(dwc, CH_EN, 1 << (p->channel_number));
}

static void rt_fh_dma_cyclic_stop(struct dma_transfer *p)
{
    struct fh81_dma *my_own = p->dma_controller;
    struct dw_dma *dwc;
    dwc = &my_own->dwc;
    channel_clear_bit(dwc, CH_EN, 1 << (p->channel_number));
}

static void rt_fh_dma_cyclic_start(struct dma_transfer *p)
{
    struct fh81_dma *my_own = p->dma_controller;
    struct dw_dma *dwc;
    dwc = &my_own->dwc;
    volatile uint32_t ret_status;
    struct dw_lli *p_lli = RT_NULL;
    p_lli                = p->first_lli;

    // 32bit ally
    RT_ASSERT(((uint32_t)p_lli & 0x03) == 0);

    dw_writel(dwc, CLEAR.XFER, 1 << (p->channel_number));
    dw_writel(dwc, CLEAR.BLOCK, 1 << (p->channel_number));
    dw_writel(dwc, CLEAR.ERROR, 1 << (p->channel_number));

    // enable isr
    channel_set_bit(dwc, MASK.BLOCK, lift_shift_bit_num(p->channel_number));
    // disable isr
    channel_clear_bit(dwc, MASK.XFER, lift_shift_bit_num(p->channel_number));

    ret_status = dw_readl(dwc, CHAN[p->channel_number].CFG_LO);
    ret_status &= ~(DWC_CFGL_RELOAD_SAR | DWC_CFGL_RELOAD_DAR);
    dw_writel(dwc, CHAN[p->channel_number].CFG_LO, ret_status);

    // set the first link add
    ret_status = dw_readl(dwc, CHAN[p->channel_number].LLP);
    ret_status = (uint32_t)&p_lli[0];
    dw_writel(dwc, CHAN[p->channel_number].LLP, ret_status);

    // set link enable
    // ret_status = dw_readl(dwc,CHAN[p->channel_number].CTL_LO);
    ret_status = DWC_CTLL_LLP_D_EN | DWC_CTLL_LLP_S_EN;
    dw_writel(dwc, CHAN[p->channel_number].CTL_LO, ret_status);

    // clear ctl_hi
    dw_writel(dwc, CHAN[p->channel_number].CTL_HI, 0);

    // enable channle
    channel_set_bit(dwc, CH_EN, 1 << (p->channel_number));
}

static void rt_fh_dma_cyclic_prep(struct fh81_dma *fh81_dma_p,
                                  struct dma_transfer *p)
{
    // bind the controller to the transfer
    p->dma_controller = fh81_dma_p;
    // bind active transfer
    fh81_dma_p->dma_channel[p->channel_number].active_trans = p;
    // p_transfer->dma_controller->dma_channel[p_transfer->channel_number].active_trans
    // = dma_trans_desc;
    struct fh81_dma *my_own = p->dma_controller;
    struct dw_dma *dwc;
    dwc = &my_own->dwc;
    volatile uint32_t ret_status;
    struct dw_lli *p_lli = RT_NULL;
    uint32_t periods, i;
    uint32_t temp_src_add = 0;
    uint32_t temp_dst_add = 0;
    uint32_t buf_len    = p->trans_len;
    uint32_t period_len = p->period_len;

    struct dma_transfer *dma_trans_desc = p;
    // check first...
    RT_ASSERT(buf_len % period_len == 0);

    // cal the periods...
    periods = buf_len / period_len;

    // get desc....
    // dma_trans_desc->first_lli = (struct dw_lli *)rt_malloc(periods *
    // sizeof(struct dw_lli));
    dma_trans_desc->first_lli = get_desc(fh81_dma_p, dma_trans_desc, periods);

    if (dma_trans_desc->first_lli == RT_NULL)
    {
        FH_DMA_DEBUG("transfer error,reason: not enough mem..\n");
        RT_ASSERT(dma_trans_desc->first_lli != RT_NULL);
    }

    rt_memset((void *)dma_trans_desc->first_lli, 0,
              periods * sizeof(struct dw_lli));
    p_lli = dma_trans_desc->first_lli;

    RT_ASSERT(((uint32_t)p_lli & 0x03) == 0);

    RT_ASSERT(dma_trans_desc->dst_inc_mode <= DW_DMA_SLAVE_FIX);
    RT_ASSERT(dma_trans_desc->src_inc_mode <= DW_DMA_SLAVE_FIX);
    // step3: set the mem..
    for (i = 0; i < periods; i++)
    {
        // parse trans para...
        // para add:
        switch (dma_trans_desc->dst_inc_mode)
        {
        case DW_DMA_SLAVE_INC:
            temp_dst_add = dma_trans_desc->dst_add +
                           i * period_len * (1 << dma_trans_desc->dst_width);
            break;
        case DW_DMA_SLAVE_DEC:
            temp_dst_add = dma_trans_desc->dst_add -
                           i * period_len * (1 << dma_trans_desc->dst_width);
            break;
        case DW_DMA_SLAVE_FIX:
            temp_dst_add = dma_trans_desc->dst_add;
            break;
        }

        switch (dma_trans_desc->src_inc_mode)
        {
        case DW_DMA_SLAVE_INC:
            temp_src_add = dma_trans_desc->src_add +
                           i * period_len * (1 << dma_trans_desc->src_width);
            break;
        case DW_DMA_SLAVE_DEC:
            temp_src_add = dma_trans_desc->src_add -
                           i * period_len * (1 << dma_trans_desc->src_width);
            break;
        case DW_DMA_SLAVE_FIX:
            temp_src_add = dma_trans_desc->src_add;
            break;
        }

        p_lli[i].sar = temp_src_add;
        p_lli[i].dar = temp_dst_add;

        // para ctl

        RT_ASSERT(dma_trans_desc->dst_width <= DW_DMA_SLAVE_WIDTH_32BIT);
        RT_ASSERT(dma_trans_desc->src_width <= DW_DMA_SLAVE_WIDTH_32BIT);

        RT_ASSERT(dma_trans_desc->dst_msize <= DW_DMA_SLAVE_MSIZE_256);
        RT_ASSERT(dma_trans_desc->src_msize <= DW_DMA_SLAVE_MSIZE_256);
        RT_ASSERT(dma_trans_desc->fc_mode <= DMA_P2P);

        p_lli[i].ctllo = DWC_CTLL_INT_EN |
                         DWC_CTLL_DST_WIDTH(dma_trans_desc->dst_width) |
                         DWC_CTLL_SRC_WIDTH(dma_trans_desc->src_width) |
                         DWC_CTLL_DST_INC_MODE(dma_trans_desc->dst_inc_mode) |
                         DWC_CTLL_SRC_INC_MODE(dma_trans_desc->src_inc_mode) |
                         DWC_CTLL_DST_MSIZE(dma_trans_desc->dst_msize) |
                         DWC_CTLL_SRC_MSIZE(dma_trans_desc->src_msize) |
                         DWC_CTLL_FC(dma_trans_desc->fc_mode) |
                         DWC_CTLL_DMS(0) | DWC_CTLL_SMS(0);
        // block size
        p_lli[i].ctlhi = period_len;

        p_lli[i].llp = (uint32_t)&p_lli[i + 1];
        p_lli[i].ctllo |= DWC_CTLL_LLP_D_EN | DWC_CTLL_LLP_S_EN;

        // flush cache to mem
        mmu_clean_invalidated_dcache((uint32_t)&p_lli[i],
                                     sizeof(struct dw_lli));

        dump_lli(&p_lli[i]);
    }
    // make a ring here
    p_lli[periods - 1].llp = (uint32_t)&p_lli[0];

    mmu_clean_invalidated_dcache((uint32_t)&p_lli[periods - 1],
                                 sizeof(struct dw_lli));

    // parse the handshake
    RT_ASSERT(dma_trans_desc->dst_hs <= DMA_SW_HANDSHAKING);
    RT_ASSERT(dma_trans_desc->src_hs <= DMA_SW_HANDSHAKING);

    // dst handshake
    dw_writel(dwc, CHAN[dma_trans_desc->channel_number].CFG_LO, 0);
    ret_status = 0;
    if (dma_trans_desc->dst_hs == DMA_SW_HANDSHAKING)
    {
        ret_status |= DWC_CFGL_HS_DST;
    }
    else
    {
        ret_status &= ~DWC_CFGL_HS_DST;
    }
    dw_writel(dwc, CHAN[dma_trans_desc->channel_number].CFG_LO, ret_status);

    // src handshake
    ret_status = dw_readl(dwc, CHAN[dma_trans_desc->channel_number].CFG_LO);
    if (dma_trans_desc->src_hs == DMA_SW_HANDSHAKING)
    {
        ret_status |= DWC_CFGL_HS_SRC;
    }
    else
    {
        ret_status &= ~DWC_CFGL_HS_SRC;
    }
    dw_writel(dwc, CHAN[dma_trans_desc->channel_number].CFG_LO, ret_status);

    // only hw handshaking need this..
    switch (dma_trans_desc->fc_mode)
    {
    case DMA_M2M:
        break;
    case DMA_M2P:
        // set dst per...
        RT_ASSERT(dma_trans_desc->dst_per < DMA_HW_HS_END);
        ret_status = dw_readl(dwc, CHAN[dma_trans_desc->channel_number].CFG_HI);
        // clear 43 ~ 46 bit
        ret_status &= ~0x7800;
        ret_status |= DWC_CFGH_DST_PER(dma_trans_desc->dst_per);
        dw_writel(dwc, CHAN[dma_trans_desc->channel_number].CFG_HI, ret_status);
        // DWC_CFGH_SRC_PER

        break;
    case DMA_P2M:
        // set src per...
        RT_ASSERT(dma_trans_desc->src_per < DMA_HW_HS_END);
        ret_status = dw_readl(dwc, CHAN[dma_trans_desc->channel_number].CFG_HI);
        // clear 39 ~ 42 bit
        ret_status &= ~0x780;
        ret_status |= DWC_CFGH_SRC_PER(dma_trans_desc->src_per);
        dw_writel(dwc, CHAN[dma_trans_desc->channel_number].CFG_HI, ret_status);

        break;
    case DMA_P2P:
        // set src and dst..
        RT_ASSERT(dma_trans_desc->dst_per < DMA_HW_HS_END);
        RT_ASSERT(dma_trans_desc->src_per < DMA_HW_HS_END);

        ret_status = dw_readl(dwc, CHAN[dma_trans_desc->channel_number].CFG_HI);
        ret_status &= ~0x7800;
        ret_status &= ~0x780;
        ret_status |= DWC_CFGH_SRC_PER(dma_trans_desc->src_per) |
                      DWC_CFGH_DST_PER(dma_trans_desc->dst_per);
        dw_writel(dwc, CHAN[dma_trans_desc->channel_number].CFG_HI, ret_status);

        break;
    default:
        break;
    }

    dma_trans_desc->dma_controller->dma_channel[dma_trans_desc->channel_number]
        .channel_status = CHANNEL_STATUS_BUSY;

    if (dma_trans_desc->prepare_callback)
    {
        dma_trans_desc->prepare_callback(dma_trans_desc->prepare_para);
    }
}

static void rt_fh_dma_cyclic_free(struct dma_transfer *p)
{
    struct fh81_dma *my_own = p->dma_controller;
    struct dw_dma *dwc;
    dwc = &my_own->dwc;
/*    volatile uint32_t ret_status;
    struct dw_lli *p_lli = RT_NULL;
    p_lli                = p->first_lli;
*/
    // close channel first..
    channel_clear_bit(dwc, CH_EN, 1 << (p->channel_number));

    // check if close really
    while (dw_readl(dwc, CH_EN) & 1 << (p->channel_number))
        ;

    dw_writel(dwc, CLEAR.XFER, 1 << (p->channel_number));
    dw_writel(dwc, CLEAR.BLOCK, 1 << (p->channel_number));
    dw_writel(dwc, CLEAR.ERROR, 1 << (p->channel_number));

    // rt_free(p->first_lli);
    put_desc(my_own, p);
}

void rt_fh_dma_init(void)
{
    fh81_dma_register(&fh81_dma_controller[0], "fh81_dma");
}
